package com.manner.groupon.service.pay.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.manner.groupon.common.BaseResponse;
import com.manner.groupon.common.ResultUtils;
import com.manner.groupon.constant.GrouponFlowTitle;
import com.manner.groupon.constant.PayMethod;
import com.manner.groupon.mapper.*;
import com.manner.groupon.model.dto.pay.PayDTO;
import com.manner.groupon.model.entity.*;
import com.manner.groupon.model.enums.OrderStatus;
import com.manner.groupon.model.enums.RoleEnums;
import com.manner.groupon.properties.WxPayProperties;
import com.manner.groupon.service.store.StoreLinkManService;
import com.manner.groupon.service.pay.PayService;
import com.manner.groupon.utils.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.TreeMap;



@Service
@Slf4j
@RequiredArgsConstructor
public class PayServiceImpl implements PayService {

    public static final String MD5_KEY = "65qlt7TbCX7im1wHe5O7XDCsKuE8sdbCnBaFn96SUdRr5repU5mwWij9qt3hMZBeLIfGW33PgKHwXSXDYAKLHUrj2vGIIOjxuGWs3o9zAww9ljggwjwU0LLQghJcFS5S";
    private static final String MCH_NO = "M1729821432";
    private static final String APP_ID = "671afaf8e4b05402541df6c6";

    private final WxPayProperties wxPayProperties;

    private final GrouponOrderMapper grouponOrderMapper;

    private final GrouponCouponsMapper grouponCouponsMapper;

    private final StoreMapper storeMapper;

    private final StoreLinkManService storeLinkManService;

    private final StoreAccountFlowMapper storeAccountFlowMapper;


    @Override
    public String pay(PayDTO payDTO) {
        String ip = IPUtil.getIp(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());
        Map signMap = new TreeMap<>();
        signMap.put("mchNo", MCH_NO);
        signMap.put("appId", APP_ID);
        signMap.put("mchOrderNo", payDTO.getOrderNo());
        signMap.put("wayCode", "WX_LITE");
        signMap.put("amount", payDTO.getTotal());
        signMap.put("currency", "cny");
        signMap.put("clientIp", ip);
        signMap.put("subject", payDTO.getDescription());
        signMap.put("body", "支付金额");
        signMap.put("notifyUrl", wxPayProperties.getNotifyUrl()); //改为自己系统的异步回调地址
        signMap.put("reqTime", System.currentTimeMillis());
        signMap.put("version", "1.0");
        signMap.put("signType", "MD5");

        String signStr = StrUtil.getSignStr(signMap);

        signStr = signStr + "&key=" + MD5_KEY;

        String sign = MD5.create().digestHex(signStr);
        signMap.put("sign", sign);
        String post = HttpUtil.post("https://pay.bozepay.com/halfscreen/api/pay/halfScreenPayApply", JSONObject.toJSONString(signMap));
        //返回参数
        return post;
    }

    @Override
    public boolean refund(GrouponOrder order) {
        String ip = IPUtil.getIp(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());

        Map signMap = new TreeMap<>();
        signMap.put("mchNo", MCH_NO);
        signMap.put("appId", APP_ID);
        signMap.put("mchOrderNo", order.getGrouponOrderId());
        signMap.put("mchRefundNo", IdUtils.generateRefundId());
        signMap.put("refundAmount", order.getTotalAmount().multiply(new BigDecimal(100)).setScale(0, RoundingMode.DOWN));
        signMap.put("currency", "cny");
        signMap.put("refundReason", "用户退款");
        signMap.put("clientIp", ip);
        signMap.put("reqTime", System.currentTimeMillis());
        signMap.put("version", "1.0");
        signMap.put("signType", "MD5");
        signMap.put("notifyUrl", wxPayProperties.getRefundNotifyUrl());

        String signStr = StrUtil.getSignStr(signMap);

        signStr = signStr + "&key=" + MD5_KEY;

        String sign = MD5.create().digestHex(signStr);
        signMap.put("sign", sign);

        log.info("Request Data {}", JSONObject.toJSONString(signMap));

        String post = HttpClientUtils.doPostJson("https://pay.bozepay.com/api/refund/refundOrder", JSONObject.toJSONString(signMap));

        return true;
    }

    @Override
    public String orderRefund(String orderNo, Integer state) {
        log.info("------收到退款通知------");
        //定义变量
        int status = OrderStatus.REFUNDED.getCode();
        //判断
        if (state.equals(2)) {
            log.info("【退款回调为SUCCESS】");
            boolean flag = updateOrder(orderNo);
            //判断
            if (!flag) {
                //修改失败
                GrouponOrder order = grouponOrderMapper.selectOne(new QueryWrapper<GrouponOrder>().eq("pay_id", orderNo));
                //修改数据
                order.setRemark("退款异常");
                order.setStatus(OrderStatus.REFUND_FAIL.getCode());
                //修改
                grouponOrderMapper.updateById(order);
            }
        } else if (state.equals(1)) {
            log.info("【退款回调为PROCESSING】");
            status = OrderStatus.REFUND_LOADING.getCode();
        }
        log.info("【最终的状态为】 : {}", status);
        updateStatus(orderNo, status);
        return "success";
    }

    @Override
    public String callback(String orderNo, String mchNo) {
        //修改订单前，主动请求微信查询订单是否支付成功，防止恶意post
        GrouponOrder order = grouponOrderMapper.selectOne(new QueryWrapper<GrouponOrder>().eq("groupon_order_id", orderNo));
        //判断订单状态
        if (ObjectUtil.isNotEmpty(order) && order.getStatus() == OrderStatus.HAVE_PAID.getCode()) {
            return "success";
        }
        //修改订单状态
        if (ObjectUtil.isNotEmpty(order)) {
            order.setPayId(mchNo);
            order.setStatus(OrderStatus.HAVE_PAID.getCode());
            //修改
            grouponOrderMapper.updateById(order);
            //修改优惠券信息
            GrouponCoupons coupons = grouponCouponsMapper.selectOne(new QueryWrapper<GrouponCoupons>().eq("id", order.getGcId()));
            //修改数量
            coupons.setSoldCount(coupons.getSoldCount() + order.getTicketNumber());
            coupons.setStock(coupons.getStock() - order.getTicketNumber());
            //修改
            grouponCouponsMapper.updateById(coupons);
            //获取利润
            Integer profit = confirmGrouponOrderProfit(coupons, coupons.getStoreId()).getData();
            //将整数利率数转换为 BigDecimal 形式的小数 50-->0.5
            BigDecimal profitRate = new BigDecimal(profit).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
            //生成流水
            generateOrderFlow(order.getTotalAmount(), profitRate, coupons.getStoreId(), order);
        }
        log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", orderNo, mchNo);
        return "success";
    }


    @Transactional(rollbackFor = Throwable.class)
    public boolean updateOrder(String no) {
        //查找数据
        GrouponOrder order = grouponOrderMapper.selectOne(new QueryWrapper<GrouponOrder>().eq("pay_id", no));
        //获取团购券
        GrouponCoupons coupons = grouponCouponsMapper.selectOne(new QueryWrapper<GrouponCoupons>().eq("id", order.getGcId()));
        //获取利润
        Integer profit = confirmGrouponOrderProfit(coupons, coupons.getStoreId()).getData();
        //将利润百分比转换为 BigDecimal 形式的小数 50-->0.5
        BigDecimal profitRate = new BigDecimal(profit).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
        //划流水
        subFlow(order.getTotalAmount(), profitRate, coupons.getStoreId(), order);
        //返回数据
        return true;
    }

    private boolean updateStatus(String no, Integer status) {
        //定义变量
        boolean flag = false;
        //查找数据
        GrouponOrder order = grouponOrderMapper.selectOne(new QueryWrapper<GrouponOrder>().eq("pay_id", no));
        order.setStatus(status);
        //修改数据
        grouponOrderMapper.updateById(order);
        flag = true;
        //返回遍历
        return flag;
    }

    /**
     * 减流水
     *
     * @param totalAmount  支付金额
     * @param profitRate   两位小数利润率数
     * @param storeNo      商家编号
     * @param grouponOrder 优惠券订单信息
     * @author Manner
     * @date 2024/10/13 0:50
     */
    public void subFlow(BigDecimal totalAmount, BigDecimal profitRate, String storeNo, GrouponOrder grouponOrder) {
        //这笔订单产生的商家利润金额
        BigDecimal storeProfitMoney = totalAmount.multiply(profitRate);
        //查询门店数据
        Store store = storeMapper.selectOne(new QueryWrapper<Store>().eq("store_no", storeNo));
        //查询商家信息
        StoreLinkMan storeLinkMan = storeLinkManService.getById(store.getAdminId());
        //查询管理员数据
        StoreLinkMan linkMan = storeLinkManService.query().eq("role", RoleEnums.ADMIN_ROLE.getId()).one();
        //更新余额
        linkMan.setBookBalance(linkMan.getBookBalance().subtract(totalAmount));
        storeLinkMan.setBookBalance(storeLinkMan.getBookBalance().subtract(storeProfitMoney));
        //更新数据库中商家的账面余额信息
        storeLinkManService.updateById(storeLinkMan);
        storeLinkManService.updateById(linkMan);
        //生成订单流水编号
        String orderFlowId = GrouponOrderIdGenerate.generateOrderFlowNumber();
        //构建订单流水记录数据
        StoreAccountFlow storeAccountFlow = StoreAccountFlow.builder()
                .storeFlowId(orderFlowId)
                .userId(grouponOrder.getUserId())
                .grouponOrderId(grouponOrder.getGrouponOrderId())
                .amount(storeProfitMoney)
                .balance(storeLinkMan.getBookBalance())
                .payMethod(PayMethod.UNION_PAY)
                .title(GrouponFlowTitle.GROUP_PURCHASE_REFUND)
                .isAdd(1)
                .build();
        //向数据库中增加订单流水记录
        storeAccountFlowMapper.insert(storeAccountFlow);
    }


    /**
     * 订单利润划账给商家账面,并增加记录订单流水记录
     *
     * @param totalAmount  支付金额
     * @param profitRate   两位小数利润率数
     * @param storeNo      商家编号
     * @param grouponOrder 优惠券订单信息
     * @author Manner
     * @date 2024/10/13 0:50
     */
    public void generateOrderFlow(BigDecimal totalAmount, BigDecimal profitRate, String storeNo, GrouponOrder grouponOrder) {
        //这笔订单产生的商家利润金额
        BigDecimal storeProfitMoney = totalAmount.multiply(profitRate);
        log.info("本笔订单的支付金额: {}", totalAmount.intValue());
        log.info("本笔订单的两位小数的分润率: {}", profitRate.intValue());
        log.info("本笔订单产生的商家利润金额: {}", storeProfitMoney);
        //查询门店数据
        Store store = storeMapper.selectOne(new QueryWrapper<Store>().eq("store_no", storeNo));
        //查询商家信息
        StoreLinkMan storeLinkMan = storeLinkManService.getById(store.getAdminId());
        //查询管理员数据
        StoreLinkMan linkMan = storeLinkManService.query().eq("role", RoleEnums.ADMIN_ROLE.getId()).one();
        //更新余额
        linkMan.setBookBalance(linkMan.getBookBalance().add(totalAmount));
        storeLinkMan.setBookBalance(storeLinkMan.getBookBalance().add(storeProfitMoney));
        //更新数据库中商家的账面余额信息
        storeLinkManService.updateById(storeLinkMan);
        storeLinkManService.updateById(linkMan);
        //生成订单流水编号
        String orderFlowId = GrouponOrderIdGenerate.generateOrderFlowNumber();
        //构建订单流水记录数据
        StoreAccountFlow storeAccountFlow = StoreAccountFlow.builder()
                .storeFlowId(orderFlowId)
                .userId(grouponOrder.getUserId())
                .grouponOrderId(grouponOrder.getGrouponOrderId())
                .amount(storeProfitMoney)
                .balance(storeLinkMan.getBookBalance())
                .payMethod(PayMethod.UNION_PAY)
                .title(GrouponFlowTitle.GROUP_BUYING_REVENUE)
                .isAdd(0)
                .build();
        //向数据库中增加订单流水记录
        storeAccountFlowMapper.insert(storeAccountFlow);
    }

    /**
     * 获取每笔订单的整数利率数
     *
     * @param grouponCoupons
     * @param storeNo
     * @return BaseResponse<Integer>
     * @author Manner
     * @date 2024/10/12 19:43
     */
    public BaseResponse<Integer> confirmGrouponOrderProfit(GrouponCoupons grouponCoupons, String storeNo) {
        //利润率(1-99)
        Integer profit;
        //判断团购优惠券的利率是否合理合规
        if (grouponCoupons.getProfit() != null && grouponCoupons.getProfit() > 0 && grouponCoupons.getProfit() <= 99) {
            //团购优惠券的利率
            profit = grouponCoupons.getProfit();
        } else {
            //商家利率
            Integer scale = storeMapper.selectOne(new LambdaQueryWrapper<Store>()
                    .eq(Store::getStoreNo, storeNo)).getScale();
            if (scale != null && scale > 0 && scale <= 99) {
                profit = scale;
            } else {
                return ResultUtils.error(500, "利率数非法!");
            }
        }
        return ResultUtils.success(profit);
    }
}
